/* ansh/main.c 
 * 
 * This file is part of ansh. 
 * 
 * ansh is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * ansh is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with ansh. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <sys/wait.h>

#include <ansh/ansh.h>


/* ansh: ansh is not sh. 
 *
 * bash is fast but not so pretty, zsh is pretty, but it's **too** slow
 * :-( so i'm going to write my shell, very simple, and use it instead 
 * of posix shells. */

char *cwd;

static char *gna_nextp;
static char *getnextarg(char *s);

static void fatal(char *s) {
	fprintf(stderr, "fatal: %s\n", s);

	exit(EXIT_FAILURE);
}

void sigint(int nr) {
	(void) nr;
}

static int init_signals(void) {
	struct sigaction act;

	memset(&act, 0, sizeof(struct sigaction));

	act.sa_handler = sigint;
	act.sa_flags = 0;

	sigaction(SIGINT, &act, NULL);

	return 0;
}

#define DEFALLOC 32

int main(int argc, const char **argv) {
	char 	*cmdline, **argtab;
	int 	i, allocated = DEFALLOC, pid, stat;

	(void) argc, (void) argv;

	if (unlikely(!(cmdline = malloc(PATH_MAX)) 
			|| unlikely(!(cwd = malloc(PATH_MAX)))) 
			|| unlikely(!(argtab = malloc(allocated * sizeof(char *))))) 
		fatal("malloc() failed");

	init_signals();
	getcwd(cwd, PATH_MAX);

	for (;;) {
		fprintf(stderr, "%s%c ", cwd, getuid() ? '$' : '#');
		
		memset(cmdline, 0, PATH_MAX);
		memset(argtab, 0, allocated * sizeof(char *));

		if (unlikely(read(0, cmdline, PATH_MAX) < 0)) {
			if (unlikely(errno != EINTR)) {
				perror("read()");
				fatal("read() failed");
			}

			printf("\n");

			continue;
		}

		if (!(argtab[0] = getnextarg(cmdline))) {
			printf("\n");

			continue;
		}

		for (i = 1; i < _POSIX_ARG_MAX; i++) {
			if (!(argtab[i] = getnextarg(NULL))) 
				break;

			if (i >= allocated) 
				fatal("too many arguments");
		}

		/* real execute */

		if (likely(do_builtin_cmd(i, argtab) == -1)) {
			if (!(pid = fork())) {
				execvp(argtab[0], argtab);

				perror(argtab[0]);
				exit(EXIT_FAILURE);
			}

			waitpid(pid, &stat, 0);

			if (WIFSIGNALED(stat)) 
				printf("exited (SIG%d) %s\n", WTERMSIG(stat), 
#ifdef WCOREDUMP
						WCOREDUMP(stat) ? " (core dumped)" : ""
#else 
						""
#endif
					);

			else if (WIFSTOPPED(stat)) 
				printf("stopped %d (SIG%d)\n", pid, WSTOPSIG(stat));

			else if (WIFCONTINUED(stat)) 
				printf("continued %d\n", pid);
		}

		if (i >= allocated) {
			free(argtab);

			if (unlikely(!(argtab = malloc(DEFALLOC * sizeof(char *))))) 
				fatal("malloc() failed");

			allocated = DEFALLOC;
		}
	}

	return 0;
}


#define isspace(_c) 	((_c) == ' ' || (_c) == '\n' || (_c) == '\t')
#define skipspaces(_s) 	{ while (isspace(*(_s))) (_s)++; }

#define gnp 		gna_nextp

#define OUT 		1
#define IN 		2

static char *getnextarg(char *s) {
	char *p;

	if (s) 
		gnp = s;

	skipspaces(gnp);

	if (!*gnp) 
		return NULL;

	for (p = gnp; *p && !isspace(*p); p++) 
		;

	*p = 0;
	s = gnp;
	gnp = ++p;

	return s;
}


